/*
 * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef DCPLUSPLUS_DCPP_FAVORITE_MANAGER_H
#define DCPLUSPLUS_DCPP_FAVORITE_MANAGER_H

#include "SettingsManager.h"

#include "Thread.h"
#ifdef IRAINMAN_ENABLE_HUB_LIST
#include "HttpConnection.h"
#endif
#include "UserCommand.h"
#include "FavoriteUser.h"
#include "Singleton.h"
#include "ClientManagerListener.h"
#include "FavoriteManagerListener.h"
#include "HubEntry.h"
#include "FavHubGroup.h"
#include "User.h"

class PreviewApplication
{
	public:
		typedef PreviewApplication* Ptr;
		typedef vector<Ptr> List;
		typedef List::const_iterator Iter;
		
		PreviewApplication() noexcept {}
		PreviewApplication(string n, string a, string r, string e) : name(n), application(a), arguments(r), extension(e) {};
		~PreviewApplication() noexcept { }
		
		GETSET(string, name, Name);
		GETSET(string, application, Application);
		GETSET(string, arguments, Arguments);
		GETSET(string, extension, Extension);
};

class SimpleXML;

/**
 * Public hub list, favorites (hub&user). Assumed to be called only by UI thread.
 */
class FavoriteManager : public Speaker<FavoriteManagerListener>,
#ifdef IRAINMAN_ENABLE_HUB_LIST
	private HttpConnectionListener,
#endif
	public Singleton<FavoriteManager>,
	private SettingsManagerListener, private ClientManagerListener
{
	public:
#ifdef IRAINMAN_ENABLE_HUB_LIST
// Public Hubs
		enum HubTypes
		{
			TYPE_NORMAL,
			TYPE_BZIP2
		};
		StringList getHubLists();
		void setHubList(int aHubList);
		int getSelectedHubList()
		{
			return lastServer;
		}
		void refresh(bool forceDownload = false);
		HubTypes getHubListType()
		{
			return listType;
		}
		HubEntryList getPublicHubs()
		{
			Lock l(cs);
			return publicListMatrix[publicListServer];
		}
		bool isDownloading()
		{
			return (useHttp && running);
		}
#endif
		
// Favorite Users
		typedef unordered_map<CID, FavoriteUser> FavoriteMap;
		FavoriteMap getFavoriteUsers()
		{
			Lock l(cs);
			return users;
		}
		PreviewApplication::List& getPreviewApps()
		{
			return previewApplications;
		}
		
		void addFavoriteUser(const UserPtr& aUser);
		bool isFavoriteUser(const UserPtr& aUser) const
		{
			Lock l(cs);
			return users.find(aUser->getCID()) != users.end();
		}
		
		// [+] SSA - add user to ban (or unset BAN)
		void addFavoriteUserToBan(const UserPtr& aUser);
		bool isFavoriteUserToBan(const UserPtr& aUser) const
		{
			Lock l(cs);
			if (!isFavoriteUser(aUser)) return false;
			return (users.find(aUser->getCID())->second.getUploadLimit() == FavoriteUser::UL_BAN);
		}
		// [+] SSA - add user to ignore (or unset ignore)
		void addFavoriteUserToIgnore(const UserPtr& aUser);
		bool isFavoriteUserToIgnore(const UserPtr& aUser) const
		{
			Lock l(cs);
			if (!isFavoriteUser(aUser)) return false;
			return (users.find(aUser->getCID())->second.isSet(FavoriteUser::FLAG_IGNOREPRIVATE));
		}
		
		
		bool getUploadLimit(const UserPtr& aUser, FavoriteUser::UPLOAD_LIMIT &ul) const;//[+]FlylinkDC
		void removeFavoriteUser(const UserPtr& aUser);
		
		bool hasSlot(const UserPtr& aUser) const;
		void setUserDescription(const UserPtr& aUser, const string& description);
		void setAutoGrant(const UserPtr& aUser, bool grant);
		void userUpdated(const OnlineUser& info);
		time_t getLastSeen(const UserPtr& aUser) const;
		std::string getUserURL(const UserPtr& aUser) const;
		
		// !SMT!-S
		bool hasBan(const UserPtr& aUser) const;
		void setUploadLimit(const UserPtr& aUser, FavoriteUser::UPLOAD_LIMIT lim);
		bool getFlag(const UserPtr& aUser, FavoriteUser::Flags) const;
		void setFlag(const UserPtr& aUser, FavoriteUser::Flags, bool);
		bool hasIgnore(const UserPtr& aUser) const
		{
			return getFlag(aUser, FavoriteUser::FLAG_IGNOREPRIVATE);
		}
		void setIgnorePrivate(const UserPtr& aUser, bool grant)
		{
			setFlag(aUser, FavoriteUser::FLAG_IGNOREPRIVATE, grant);
		}
		bool hasFreePM(const UserPtr& aUser) const
		{
			return getFlag(aUser, FavoriteUser::FLAG_FREE_PM_ACCESS);
		}
		void setFreePM(const UserPtr& aUser, bool grant)
		{
			setFlag(aUser, FavoriteUser::FLAG_FREE_PM_ACCESS, grant);
		}
		void setSuperUser(const UserPtr& aUser, bool superUser);
		
// Favorite Hubs
		FavoriteHubEntryList& getFavoriteHubs()
		{
			return favoriteHubs;
		}
		
		enum AutoStartType
		{
			REMOVE = -1,
			NOT_CHANGE = 0,
			ADD = 1,
		};

		void addFavorite(const FavoriteHubEntry& aEntry, const AutoStartType p_autostart = NOT_CHANGE);
		void removeFavorite(const FavoriteHubEntry* entry);
		bool isFavoriteHub(const std::string& aUrl);
		FavoriteHubEntry* getFavoriteHubEntry(const string& aServer) const;
		
		bool isPrivate(const string& p_url) const;
// Favorite hub groups
		const FavHubGroups& getFavHubGroups() const
		{
			return favHubGroups;
		}
		void setFavHubGroups(const FavHubGroups& favHubGroups_)
		{
			favHubGroups = favHubGroups_;
		}
		
		FavoriteHubEntryList getFavoriteHubs(const string& group) const;
		
// Favorite Directories
		struct FavoriteDirectory
		{
			string dir, ext, name;
		};
		typedef vector<FavoriteDirectory> FavDirList;
		typedef FavDirList::iterator FavDirIter;
		bool addFavoriteDir(const string& aDirectory, const string& aName, const string& aExt);
		bool removeFavoriteDir(const string& aName);
		bool renameFavoriteDir(const string& aName, const string& anotherName);
		bool updateFavoriteDir(const string& aName, const FavoriteDirectory& dir);
		bool moveFavoriteDir(int cid, int pos); // [+] InfinitySky.
		string getDownloadDirectory(const string& ext);
		FavDirList getFavoriteDirs()
		{
			return favoriteDirs;
		}
		
// Recent Hubs
		RecentHubEntry::List& getRecentHubs()
		{
			return recentHubs;
		};
		
		void addRecent(const RecentHubEntry& aEntry);
		void removeRecent(const RecentHubEntry* entry);
		void updateRecent(const RecentHubEntry* entry);
		
		RecentHubEntry* getRecentHubEntry(const string& aServer)
		{
			for (RecentHubEntry::Iter i = recentHubs.begin(); i != recentHubs.end(); ++i)
			{
				RecentHubEntry* r = *i;
				if (stricmp(r->getServer(), aServer) == 0)
				{
					return r;
				}
			}
			return NULL;
		}
		
		PreviewApplication* addPreviewApp(string name, string application, string arguments, string extension)
		{
			PreviewApplication* pa = new PreviewApplication(name, application, arguments, extension);
			previewApplications.push_back(pa);
			return pa;
		}
		
		PreviewApplication* removePreviewApp(size_t index)
		{
			if (previewApplications.size() > index)
				previewApplications.erase(previewApplications.begin() + index);
			return NULL;
		}
		
		PreviewApplication* getPreviewApp(size_t index, PreviewApplication &pa)
		{
			if (previewApplications.size() > index)
				pa = *previewApplications[index];
			return NULL;
		}
		
		PreviewApplication* updatePreviewApp(size_t index, PreviewApplication &pa)
		{
			*previewApplications[index] = pa;
			return NULL;
		}
		
		void removeallRecent()
		{
			recentHubs.clear();
			recentsave();
		}
		
// User Commands
		UserCommand addUserCommand(int type, int ctx, Flags::MaskType flags, const string& name, const string& command, const string& to, const string& hub);
		bool getUserCommand(int cid, UserCommand& uc);
		int findUserCommand(const string& aName, const string& aUrl);
		bool moveUserCommand(int cid, int pos);
		void updateUserCommand(const UserCommand& uc);
		void removeUserCommand(int cid);
		void removeUserCommand(const string& srv);
		void removeHubUserCommands(int ctx, const string& hub);
		
		UserCommand::List getUserCommands()
		{
			Lock l(cs);
			return userCommands;
		}
		UserCommand::List getUserCommands(int ctx, const StringList& hub, bool& op);
		
		void load();
		void save();
		void recentsave();
		
		// [+] Flylink
		bool checkFavHubExists(const FavoriteHubEntry& aEntry);

	private:
		FavoriteHubEntryList favoriteHubs;
		FavDirList favoriteDirs; // [~] InfinitySky. Code from Apex.
		FavHubGroups favHubGroups;
		RecentHubEntry::List recentHubs;
		PreviewApplication::List previewApplications;
		UserCommand::List userCommands;
		int lastId;
		
		FavoriteMap users;
		
		mutable CriticalSection cs;
		
#ifdef IRAINMAN_ENABLE_HUB_LIST
		// Public Hubs
		typedef unordered_map<string, HubEntryList> PubListMap;
		PubListMap publicListMatrix;
		string publicListServer;
		bool useHttp, running;
		HttpConnection* c;
		int lastServer;
		HubTypes listType;
		string downloadBuf;
#endif
		
		/** Used during loading to prevent saving. */
		bool dontSave;
		
		friend class Singleton<FavoriteManager>;
		
		FavoriteManager();
		~FavoriteManager();
		
		FavoriteHubEntryList::const_iterator getFavoriteHub(const string& aServer) const;
		RecentHubEntry::Iter getRecentHub(const string& aServer) const;
		
		// ClientManagerListener
		void on(UserUpdated, const OnlineUserPtr& user) noexcept;
		void on(UserConnected, const UserPtr& user) noexcept;
		void on(UserDisconnected, const UserPtr& user) noexcept;
		
#ifdef IRAINMAN_ENABLE_HUB_LIST
		// HttpConnectionListener
		void on(Data, HttpConnection*, const uint8_t*, size_t) noexcept;
		void on(Failed, HttpConnection*, const string&) noexcept;
		void on(Complete, HttpConnection*, const string&
#ifdef RIP_USE_CORAL
		        , bool
#endif
		       ) noexcept;
		void on(Redirected, HttpConnection*, const string&) noexcept;
		void on(TypeNormal, HttpConnection*) noexcept;
		void on(TypeBZ2, HttpConnection*) noexcept;
		void on(Retried, HttpConnection*, const bool) noexcept;
		
		bool onHttpFinished(bool fromHttp) noexcept;
#endif
		
		// SettingsManagerListener
		void on(SettingsManagerListener::Load, SimpleXML& xml) noexcept
		{
			load(xml);
			recentload(xml);
			previewload(xml);
		}
		
		void on(SettingsManagerListener::Save, SimpleXML& xml) noexcept
		{
			previewsave(xml);
		}
		
		void load(SimpleXML& aXml);
		void recentload(SimpleXML& aXml);
		void previewload(SimpleXML& aXml);
		void previewsave(SimpleXML& aXml);
		
		string getConfigFile()
		{
			return Util::getConfigPath() + "Favorites.xml";
		}
		
		// [+] SSA addUser
		bool _addUserIfnotExist(const UserPtr& aUser, FavoriteMap::iterator& iUser);

		
};

#endif // !defined(FAVORITE_MANAGER_H)

/**
 * @file
 * $Id: FavoriteManager.h 568 2011-07-24 18:28:43Z bigmuscle $
 */
